﻿/* 
 *	Copyright (C) 2010- ZWaveApi
 *	http://ZWaveApi.net
 *
 *  This Program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as published by
 *  the Free Software Foundation; either version 3, or (at your option)
 *  any later version.
 *   
 *  This Program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *   
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with GNU Make; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. 
 *  http://www.gnu.org/copyleft/lesser.html
 *
 */

using System;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.Collections.Generic;
using System.Configuration;
using System.Threading;
using System.Diagnostics;
using ZWaveApi.Net;
using ZWaveApi.Net.CommandClasses;
using System.Web;

namespace ZWaveApi.Web
{
    [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single)]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class ZWaveApiService : IZWaveApiServer, ICommandClass
    {
        private ZWaveController zc;

        public static List<IZwaveApiCallback> ZwaveApiCallback = new List<IZwaveApiCallback>();

        public ZWaveApiService()
        {
            if (ZWaveLog.toWcf())
            {
                ZWaveLog.LogHandler logHandler = new ZWaveLog.LogHandler(LogUpdate);
                ZWaveLog.SubscribeToMessages(logHandler);
            }

            string comPort = ConfigurationManager.AppSettings.Get("ComPort");
            zc = new ZWaveController(comPort);
        }

        #region Callback Members

        /// <summary>
        /// This handler is call from ZWavePort, and call 
        ///   - MessageRequestHandler if it is a request
        ///   - MessageResponseHandler if it is a Response
        /// </summary>
        /// <param name="sentMessage">The message that we send to the ZWave</param>
        /// <param name="receivedMessage">The message that we received from the ZWave</param>
        protected virtual void MessageHandler(ZWaveApi.Net.Messenger.Message sentMessage, ZWaveApi.Net.Messenger.Message receivedMessage)
        {
            if (receivedMessage.frameHeader != ZWaveApi.Net.Messenger.FrameHeader.ACK &&
                receivedMessage.buffer.Length > 3 &&
                receivedMessage.messageType == ZWaveApi.Net.Messenger.MessageType.Request &&
                receivedMessage.function == ZWaveFunction.ApplicationCommandHandler)
            {
                ZWaveUpdates(receivedMessage.nodeId, zc.Nodes[receivedMessage.nodeId].HandleMessage(receivedMessage));
            }
        }

        private void ZWaveUpdates(byte nodeID, CommandClass commandClass)
        {
            ZwaveApiCallback.ForEach(
                delegate(IZwaveApiCallback client)
                { client.NodeIsUpdate(nodeID, commandClass); });
        }

        protected void LogUpdate(string msg)
        {
            if (ZWaveLog.toWcf())
            {
                ZwaveApiCallback.ForEach(
                    delegate(IZwaveApiCallback client)
                    { client.LogIsUpdate(msg); });
            }
        }

        #endregion

        #region IZWaveApiServer Members

        public void RegisterForUpdates()
        {
            IZwaveApiCallback c = OperationContext.Current.GetCallbackChannel<IZwaveApiCallback>();
            if (!ZwaveApiCallback.Contains(c))
            {
                lock (ZwaveApiCallback)
                {
                    ZwaveApiCallback.Add(c);
                }
            }
        }

        public void UnregisterForUpdates()
        {
            IZwaveApiCallback c = OperationContext.Current.GetCallbackChannel<IZwaveApiCallback>();
            if (ZwaveApiCallback.Contains(c))
            {
                lock (ZwaveApiCallback)
                {
                    ZwaveApiCallback.Remove(c);
                }
            }
        }

        public bool Open()
        {
            bool isOpen = zc.Open();

            if (isOpen)
            {
                ZWavePort.MessageHandler messageHandler = new ZWavePort.MessageHandler(MessageHandler);
                zc.Port.SubscribeToMessages(messageHandler);

            }

            return isOpen;
        }

        public bool IsOpen()
        {
            return zc.Port.IsOpen();
        }

        public void Close()
        {
            zc.Close();
        }

        public List<ZWaveNode> GetAllNodes()
        {
            if (IsOpen())
            {
                List<ZWaveNode> Nodes = new List<ZWaveNode>(zc.Nodes.Values);

                return Nodes;
            }
            else
            {
                return null;
            }
        }

        public ZWaveNode GetNode(byte nodeId)
        {
            if (IsOpen())
                return zc.Nodes[nodeId];
            else
                return null;
        }

        public void GetAllNodesState()
        {
            if (IsOpen())
            {
                foreach (ZWaveNode node in zc.Nodes.Values)
                {
                    GetNodeState(node.NodeId);
                }
            }
        }

        public void GetNodeState(byte nodeId)
        {
            if (IsOpen())
            {
                foreach (GenericCommandClass gcc in zc.Nodes[nodeId].ListOfCommandClasses)
                {
                    gcc.RequestState();
                }
            }
        }

        public ZWaveInfo Info()
        {
            if (IsOpen())
                return zc.Info;
            else
                return null;
        }

        #endregion

        #region ICommandClass Members

        public List<GenericCommandClass> GetAllCommandClass(byte nodeId)
        {
 //                       List<GenericCommandClass> tmp = new List<GenericCommandClass>(zc.Nodes[nodeId].CommandClasses.Values);

            if (IsOpen())
                return zc.Nodes[nodeId].ListOfCommandClasses;
            else
                return null;
        }

        public GenericCommandClass GetCommandClass(byte nodeId, CommandClass commandClass)
        {
            if (IsOpen())
            {
                foreach (GenericCommandClass gcc in zc.Nodes[nodeId].ListOfCommandClasses)
                {
                    if (gcc.CommandClass == commandClass)
                    {
                        return gcc;
                    }
                }
            }
            return null;
        }

        public void PutCommandClass(byte nodeId, GenericCommandClass commandClass)
        {
            if (IsOpen())
            {
                switch (commandClass.CommandClass)
                {
                    case CommandClass.SwitchBinary:
                        ((SwitchBinary)GetCommandClass(nodeId: nodeId, commandClass: commandClass.CommandClass)).Toggle();
                        break;
                    default:
                        break;
                }

            }
        }

        public void RequestState(byte nodeId, GenericCommandClass commandClass)
        {
            GetCommandClass(nodeId: nodeId, commandClass: commandClass.CommandClass).RequestState();
        }

        #endregion
    }
}
